﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Runtime.InteropServices;
using Aeon.Emulator.Dos.VirtualFileSystem;

namespace Aeon.Emulator.Dos.Programs
{
    /// <summary>
    /// Contains a loaded com file.
    /// </summary>
    internal sealed class ComFile : ProgramImage
    {
        /// <summary>
        /// File name of the command interpreter.
        /// </summary>
        public static readonly VirtualPath CommandPath = new VirtualPath(@"COMMAND.COM");
        /// <summary>
        /// COMMAND.COM implementation.
        /// </summary>
        public static readonly byte[] CommandInterpreter = Properties.Resources.Command;

        /// <summary>
        /// Data read from the stream.
        /// </summary>
        private byte[] imageData;

        /// <summary>
        /// Initializes a new instance of the ComFile class.
        /// </summary>
        /// <param name="path">The full path to the file.</param>
        /// <param name="stream">A stream backed by the file.</param>
        public ComFile(VirtualPath path, Stream stream)
            : base(path, stream)
        {
        }

        /// <summary>
        /// Gets the com file image.
        /// </summary>
        public ReadOnlyCollection<byte> Image
        {
            get { return Array.AsReadOnly(imageData); }
        }

        /// <summary>
        /// Gets the maximum number of paragraphs to allocate when the program is loaded.
        /// </summary>
        internal override ushort MaximumParagraphs
        {
            get { return 0xFFFF; }
        }

        /// <summary>
        /// Loads the program image into a virtual machine's address space and sets
        /// any processor registers required to run the image.
        /// </summary>
        /// <param name="vm">Virtual machine to load the image into.</param>
        /// <param name="dataSegment">Default data segment for the program and the location of its PSP.</param>
        internal override void Load(VirtualMachine vm, ushort dataSegment)
        {
            if(vm == null)
                throw new ArgumentNullException("vm");

            vm.WriteSegmentRegister(SegmentIndex.CS, dataSegment);
            vm.WriteSegmentRegister(SegmentIndex.DS, dataSegment);
            vm.WriteSegmentRegister(SegmentIndex.ES, dataSegment);
            vm.WriteSegmentRegister(SegmentIndex.SS, dataSegment);
            vm.Processor.SP = 0xFFFE;
            vm.Processor.DI = 0xFFFE;
            vm.Processor.BP = 0x091C;
            vm.Processor.IP = 0x0100;
            vm.Processor.SI = 0x0100;
            vm.Processor.AX = 0;
            vm.Processor.BX = 0;
            vm.Processor.CX = 0x00FF;
            vm.Processor.DX = (short)dataSegment;

            var ptr = vm.PhysicalMemory.GetPointer(vm.Processor.CS, vm.Processor.IP);
            Marshal.Copy(imageData, 0, ptr, imageData.Length);
        }
        /// <summary>
        /// Loads the program image as an overlay at a specified address.
        /// </summary>
        /// <param name="vm">Virtual machine to load the image into.</param>
        /// <param name="overlaySegment">Segment to load the overlay.</param>
        /// <param name="relocationFactor">Value to add to relocation segments.</param>
        internal override void LoadOverlay(VirtualMachine vm, ushort overlaySegment, int relocationFactor)
        {
            throw new NotSupportedException();
        }
        /// <summary>
        /// Reads the com file from a stream.
        /// </summary>
        /// <param name="stream">Stream from which image is read.</param>
        internal override void Read(Stream stream)
        {
            int length = (int)stream.Length;
            imageData = new byte[length];
            stream.Read(imageData, 0, length);
        }
    }
}
